<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!--

  	
 
  Copyright  2006 Sun Microsystems, Inc. All rights reserved.

-->
<html>
  <head>
    <title></title>
    <!-- Changed  29-Aug-2001 -->
  </head>
  
  <body bgcolor="#FFFFFF">
    
    The Mobile Information Device Profile provides a mechanism for
    MIDlets to persistently store data and later retrieve it. This
    persistent storage mechanism is modeled after a simple record oriented
    database and is called the Record Management System.
    <H2>Persistent Storage</H2>

    <P>
      The MIDP provides a mechanism for MIDlets to persistently store data
      and retrieve it later. This persistent storage mechanism, called the
      Record Management System (RMS), is modeled after a simple
      record-oriented database.</P>
    
    
    <H3>Record Store</H3>
    <P>
      A record store consists of a collection of records that will remain
      persistent across multiple invocations of a MIDlet. The platform is
      responsible for making its best effort to maintain the integrity of the
      MIDlet's record stores throughout the normal use of the platform,
      including reboots, battery changes, etc.</P>
    <P>
      Record stores are created in platform-dependent locations, which
      are not exposed to MIDlets. The naming space for record stores
      is controlled at the MIDlet suite granularity. MIDlets within a
      MIDlet suite are allowed to create multiple record stores, as
      long as they are each given different names. When a MIDlet suite
      is removed from a platform, all record stores associated with
      its MIDlets MUST also be removed. MIDlets within a MIDlet suite
      can access one another's record stores directly. New APIs in
      MIDP allow for the explicit sharing of record stores if the
      MIDlet creating the RecordStore chooses to give such
      permission.</P>

      <p>
	Sharing is accomplished through the ability to name a
	RecordStore in another MIDlet suite and by defining the
	accessibility rules related to the Authentication of the two
	MIDlet suites.</p>
 
      <P>
	RecordStores are uniquely named using the unique name of the
	MIDlet suite plus the name of the RecordStore. MIDlet suites
	are identified by the MIDlet-Vendor and MIDlet-Name attributes
	from the application descriptor.</p>
 
      <p>
	Access controls are defined when RecordStores to be shared are
	created. Access controls are enforced when RecordStores are
	opened. The access modes allow private use or
	shareable with any other MIDlet suite.</p>

    <P>
      Record store names are case sensitive and may consist of any
      combination of up to 32 Unicode characters. Record store names MUST be
      unique within the scope of a given MIDlet suite. In other words,
      MIDlets within a MIDlet suite are not allowed to create more than one
      record store with the same name; however, a MIDlet in one MIDlet suite
      is allowed to have a record store with the same name as a MIDlet in
      another MIDlet suite. In that case, the record stores are still
      distinct and separate.</P>
    <P>
      No locking operations are provided in this API. Record store
      implementations ensure that all individual record store
      operations are atomic, synchronous, and serialized so that no
      corruption occurs with multiple accesses. However, if a MIDlet
      uses multiple threads to access a record store, it is the
      MIDlet's responsibility to coordinate this access, or unintended
      consequences may result. For example, if two threads in a MIDlet
      both call <CODE>RecordStore.setRecord()</CODE> concurrently on
      the same record, the record store will serialize these calls
      properly, and no database corruption will occur as a result.
      However, one of the writes will be subsequently overwritten by
      the other, which may cause problems within the
      MIDlet. Similarly, if a platform performs transparent
      synchronization of a record store or other access from below, it
      is the platform's responsibility to enforce exclusive access to
      the record store between the MIDlets and synchronization
      engine.</P>
    <P>
      This record store API uses long integers for time/date stamps,
      in the format used by <CODE>System.currentTimeMillis()</CODE>
      . The record store is time stamped with the last time it was
      modified. The record store also maintains a version, which is an
      integer that is incremented for each operation that modifies the
      contents of the record store. These are useful for
      synchronization engines as well as applications.</P>
    
    
    <H3>Records</H3>
    <P>
      Records are arrays of bytes. Developers can use
      <CODE>DataInputStream</CODE> and <CODE>DataOutputStream</CODE>
      as well as <CODE>ByteArrayInputStream</CODE> and
      <CODE>ByteArrayOutputStream</CODE> to pack and unpack different
      data types into and out of the byte arrays. </P>
    <P>
      Records are uniquely identified within a given record store by
      their <CODE>recordId</CODE> , which is an integer value. This
      <CODE>recordId</CODE> is used as the primary key for the
      records. The first record created in a record store will have
      <CODE>recordId</CODE> equal to 1, and each subsequent
      <CODE>recordId</CODE> will monotonically increase by one. For
      example, if two records are added to a record store, and the
      first has a <CODE>recordId</CODE> of 'n', the next will have a
      <CODE>recordId</CODE> of (n+1). MIDlets can create other indices
      by using the <CODE>RecordEnumeration</CODE> class.</P>
    
    
<h3>Example:</h3>
<p>The example uses the Record Management System to store and retrieve
  high scores for a game. In the example, high scores are stored in
  separate records, and sorted when necessary using a RecordEnumeration.</p>

<pre>

import javax.microedition.rms.*;
import java.io.DataOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.EOFException;

/**
 * A class used for storing and showing game scores.
 */
public class RMSGameScores
    implements RecordFilter, RecordComparator
{
    /*
     * The RecordStore used for storing the game scores.
     */
    private RecordStore recordStore = null;

    /*
     * The player name to use when filtering.
     */
    public static String playerNameFilter = null;

    /*
     * Part of the RecordFilter interface.
     */
    public boolean matches(byte[] candidate)
	throws IllegalArgumentException
    {
	// If no filter set, nothing can match it.
	if (this.playerNameFilter == null) {
	    return false;
	}

	ByteArrayInputStream bais = new ByteArrayInputStream(candidate);
	DataInputStream inputStream = new DataInputStream(bais);
	String name = null;
	
	try {
	    int score = inputStream.readInt();
	    name = inputStream.readUTF();
	}
	catch (EOFException eofe) {
	    System.out.println(eofe);
	    eofe.printStackTrace();
	}
	catch (IOException eofe) {
	    System.out.println(eofe);
	    eofe.printStackTrace();
	}
	return (this.playerNameFilter.equals(name));
    }

    /*
     * Part of the RecordComparator interface.
     */
    public int compare(byte[] rec1, byte[] rec2)
    {
	// Construct DataInputStreams for extracting the scores from
	// the records.
	ByteArrayInputStream bais1 = new ByteArrayInputStream(rec1);
	DataInputStream inputStream1 = new DataInputStream(bais1);
	ByteArrayInputStream bais2 = new ByteArrayInputStream(rec2);
	DataInputStream inputStream2 = new DataInputStream(bais2);
	int score1 = 0;
	int score2 = 0;
	try {
	    // Extract the scores.
	    score1 = inputStream1.readInt();
	    score2 = inputStream2.readInt();
	}
	catch (EOFException eofe) {
	    System.out.println(eofe);
	    eofe.printStackTrace();
	}
	catch (IOException eofe) {
	    System.out.println(eofe);
	    eofe.printStackTrace();
	}

	// Sort by score
	if (score1 < score2) {
	    return RecordComparator.PRECEDES;
	}
	else if (score1 > score2) {
	    return RecordComparator.FOLLOWS;
	}
	else {
	    return RecordComparator.EQUIVALENT;
	}
    }

    /**
     * The constructor opens the underlying record store,
     * creating it if necessary.
     */
    public RMSGameScores()
    {
	//
	// Create a new record store for this example
	//
	try {
	    recordStore = RecordStore.openRecordStore("scores", true);
	}
	catch (RecordStoreException rse) {
	    System.out.println(rse);
	    rse.printStackTrace();
	}
    }

    /**
     * Add a new score to the storage.
     *
     * @param score the score to store.
     * @param playerName the name of the play achieving this score.
     */
    public void addScore(int score, String playerName)
    {
	//
	// Each score is stored in a separate record, formatted with
	// the score, followed by the player name.
	//
        int recId;  // returned by addRecord but not used
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	DataOutputStream outputStream = new DataOutputStream(baos);
	try {
	    // Push the score into a byte array.
	    outputStream.writeInt(score);
	    // Then push the player name.
	    outputStream.writeUTF(playerName);
	}
	catch (IOException ioe) {
	    System.out.println(ioe);
	    ioe.printStackTrace();
	}

	// Extract the byte array
	byte[] b = baos.toByteArray();
	// Add it to the record store
	try {
	    recId = recordStore.addRecord(b, 0, b.length);
	}
	catch (RecordStoreException rse) {
	    System.out.println(rse);
	    rse.printStackTrace();
	}
    }

    /**
     * A helper method for the printScores methods.
     */
    private void printScoresHelper(RecordEnumeration re)
    {
	try {
	    while(re.hasNextElement()) {
		int id = re.nextRecordId();
		ByteArrayInputStream bais = new ByteArrayInputStream(recordStore.getRecord(id));
		DataInputStream inputStream = new DataInputStream(bais);
		try {
		    int score = inputStream.readInt();
		    String playerName = inputStream.readUTF();
		    System.out.println(playerName + " = " + score);
		}
		catch (EOFException eofe) {
		    System.out.println(eofe);
		    eofe.printStackTrace();
		}
	    }
	}
	catch (RecordStoreException rse) {
	    System.out.println(rse);
	    rse.printStackTrace();
	}
	catch (IOException ioe) {
	    System.out.println(ioe);
	    ioe.printStackTrace();
	}
    }

    /**
     * This method prints all of the scores sorted by game score.
     */
    public void printScores()
    {
	try {
	    // Enumerate the records using the comparator implemented
	    // above to sort by game score.
	    RecordEnumeration re = recordStore.enumerateRecords(null, this, 
								true);
	    printScoresHelper(re);
	}
	catch (RecordStoreException rse) {
	    System.out.println(rse);
	    rse.printStackTrace();
	}
    }

    /**
     * This method prints all of the scores for a given player,
     * sorted by game score.
     */
    public void printScores(String playerName)
    {
	try {
	    // Enumerate the records using the comparator and filter
	    // implemented above to sort by game score.
	    RecordEnumeration re = recordStore.enumerateRecords(this, this, 
								true);
	    printScoresHelper(re);
	}
	catch (RecordStoreException rse) {
	    System.out.println(rse);
	    rse.printStackTrace();
	}
    }

    public static void main(String[] args)
    {
	RMSGameScores rmsgs = new RMSGameScores();
	rmsgs.addScore(100, "Alice");
	rmsgs.addScore(120, "Bill");
	rmsgs.addScore(80, "Candice");
	rmsgs.addScore(40, "Dean");
	rmsgs.addScore(200, "Ethel");
	rmsgs.addScore(110, "Farnsworth");
	rmsgs.addScore(220, "Farnsworth");
	System.out.println("All scores");
	rmsgs.printScores();
	System.out.println("Farnsworth's scores");
	RMSGameScores.playerNameFilter = "Farnsworth";
	rmsgs.printScores("Farnsworth");
    }
}
</pre>
@since MIDP 1.0
</body>
</html>
